﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://wpfimageviewer.codeplex.com
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using BrainTechLLC.WPF;
using System.Drawing;
using AForge.Imaging.Filters;
using BrainTechLLC;
using BrainTechLLC.FilterExtensions;
using BrainTechLLC.ImageOperators;
using AForge.Imaging;
using System.Diagnostics;
using BrainTechLLC.ImageOperatorsNonAForgeDependent;

namespace ImageAppDataModels
{
    public partial class FileImage : BaseNotifyPropertyChangedUI, ICanBeFiltered, INotifyPropertyChanged, IObjectLock, IFileImage
    {
        public Bitmap _transformedBitmap;
        public int _transformedWidth;
        public int _transformedHeight;
        protected FilterCollection _filters;

        public virtual FilterCollection Filters
        {
            get { return _filters; }
            set
            {
                if (_filters != value)
                {
                    //DetachFilterEvents(_filters);
                    _filters = value;
                    //AttachFilterEvents(_filters);
                    OnPropertyChanged("Filters");
                }
            }
        }

        public void AttachFilterEvents(FilterCollection filterCollection)
        {
            if (filterCollection != null)
            {
                filterCollection.CollectionChanged += new NotifyCollectionChangedEventHandler(_filters_CollectionChanged);
                filterCollection.GenerationChanged += new EventHandler<NotifyCollectionChangedEventArgs>(_filters_GenerationChanged);
            }
        }

        public void DetachFilterEvents(FilterCollection filterCollection)
        {
            if (filterCollection != null)
            {
                filterCollection.CollectionChanged -= _filters_CollectionChanged;
                filterCollection.GenerationChanged -= _filters_GenerationChanged;
            }
        }

        public Bitmap TransformedBitmapCached
        {            
            get { return _transformedBitmap; }
        }

        public Bitmap GetTransformedBitmap(int width, int height, bool doNotRaiseImageSourceChanged)
        {
            Bitmap transformed = TransformedBitmapCached;

            if (transformed != null)
            {
                double multiplier = Scaling.GetScaledImageDimensions(_originalBitmapWidth, _originalBitmapHeight, width, height);
                int wid = (int)((double)_originalBitmapWidth * multiplier);
                int hei = (int)((double)_originalBitmapHeight * multiplier);

                if (wid == _transformedWidth && hei == _transformedHeight)
                {
                    return transformed;
                }

                wid = (int)Math.Min(wid, (double)_originalBitmapWidth);
                hei = (int)Math.Min(hei, (double)_originalBitmapHeight);

                if (wid == _transformedWidth && hei == _transformedHeight)
                {
                    return transformed;
                }
            }

            Bitmap bm = BitmapCached;

            if (bm == null)
            {
                bm = InitiateBitmapLoad();
            }

            if (bm != null)
            {
                double multiplier = Scaling.GetScaledImageDimensions(_originalBitmapWidth, _originalBitmapHeight, width, height);
                int wid = (int)((double)_originalBitmapWidth * multiplier);
                int hei = (int)((double)_originalBitmapHeight * multiplier);

                if (NeedsRefreshing(wid, hei, bm))
                {
                    // could be yanked ref from beneath us... use Bitmap?
                    if (_filters == null || _filters.Count == 0 || !_filters.Active || _filters.EnabledFiltersCount == 0)
                    {
                        transformed = GetTransformedNoFilters(width, height, wid, hei, doNotRaiseImageSourceChanged, transformed, bm);
                    }
                    else
                    {
                        transformed = GetTransformedApplyFilters(width, height, doNotRaiseImageSourceChanged, transformed, bm);
                    }
                }
            }

            return transformed;
        }

        private bool NeedsRefreshing(int width, int height, Bitmap bm)
        {            
            return (_transformedWidth != width || _transformedHeight != height || _transformedBitmap == null) && bm != null;
        }

        private Bitmap GetTransformedApplyFilters(int width, int height, bool doNotRaiseImageSourceChanged, Bitmap transformed, Bitmap bm)
        {
            try
            {
                transformed = ApplyFilters(bm, _filters, bm.Width, bm.Height, width, height);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                Debug.WriteLine(ex.ToString());
                Debug.Assert(false);
            }

            if (TransformedBitmapCached != transformed)
            {
                if (doNotRaiseImageSourceChanged)
                {
                    SetSuspendPropertyChanged(true);
                }

                TransformedBitmap = transformed;

                if (doNotRaiseImageSourceChanged)
                {
                    SetSuspendPropertyChanged(false);
                }
                else
                {
                    OnPropertyChangedUIThread("ImageSource");
                }
            }
            return transformed;
        }

        private Bitmap GetTransformedNoFilters(int width, int height, int wantWidth, int wantHeight, bool doNotRaiseImageSourceChanged, Bitmap transformed, Bitmap bm)
        {
            RenderSettings renderSettings;
            // Optimize
            if (Settings.Values.StandardColorPixelFormat != bm.PixelFormat)
            {
                renderSettings = new RenderSettings()
                {
                    PixelFormat = bm.PixelFormat,
                    Interpolation = Settings.Values.InterpolationMode,
                    Smoothing = Settings.Values.SmoothingMode,
                    RenderingMethod = Settings.Values.InternalRendering
                };
            }
            else
            {
                renderSettings = Settings.Values.StandardRenderSettings;
            }

            if (((double)_originalBitmapWidth).Within(wantWidth, 0.75) && ((double)_originalBitmapHeight).Within(wantHeight, 0.75))
            {
                //transformed = bm.Clone() as Bitmap; // HEREHERE
                transformed = bm;
            }
            else
            {
                transformed = bm.CreateBitmap(width, height, true, renderSettings) as Bitmap;
            }

            if (TransformedBitmapCached != transformed)
            {
                if (doNotRaiseImageSourceChanged)
                {
                    SetSuspendPropertyChanged(true);
                }

                // may need a lock around this...
                TransformedBitmap = transformed;

                if (doNotRaiseImageSourceChanged)
                {
                    SetSuspendPropertyChanged(false);
                }

                if (!doNotRaiseImageSourceChanged)
                {
                    OnPropertyChanged("ImageSource");
                }
            }
            return transformed;
        }

        public Bitmap TransformedBitmap
        {
            get
            {
                if (_transformedBitmap == null) 
                {
                    Bitmap bm = BitmapCached;

                    if (bm == null)
                    {
                        bm = InitiateBitmapLoad();
                    }

                    int width = (bm == null) ? 0 : bm.Width;
                    int height = (bm == null) ? 0 : bm.Height;

                    GetTransformedBitmap(width, height, false);
                }

                return _transformedBitmap;
            }
            set
            {
                if (_transformedBitmap != value)
                {
                    if (_transformedBitmap != null)
                    {
                        _transformedBitmap.Dispose();
                    }

                    if (value == null)
                    {
                        _transformedBitmap = null;
                    }
                    else
                    {
                        _transformedBitmap = value;
                        _transformedWidth = _transformedBitmap.Width;
                        _transformedHeight = _transformedBitmap.Height;
                        RecentlyUsedFileImages.RecentlyUsedTransformedBitmaps.MarkItemRecentlyUsed(this);
                    }

                    OnPropertyChanged("TransformedBitmap");
                }
            }
        }

        public Bitmap ApplyFilters(Bitmap originalBitmap, FilterCollection filters, int origWidth, int origHeight, int width, int height)
        {
            int widthOut = 0; int heightOut = 0;
            List<RGBAndCoordinate> postProcessInfo = null;

            double multiplier = Scaling.GetScaledImageDimensions(origWidth, origHeight, width, height);
            int wid;
            int hei;
            // Scaling.GetScaledSize(origWidth, origHeight, width, height, out wid, out hei);
            wid = (int)((double)origWidth * multiplier);
            hei = (int)((double)origHeight * multiplier);
            wid = Math.Min(wid, origWidth);
            hei = Math.Min(hei, origHeight);

            return ApplyFilters(originalBitmap, filters,
                origWidth,
                origHeight,
                wid,
                hei, false, ref postProcessInfo, ref widthOut, ref heightOut);
        }

        public Bitmap ApplyFilters(Bitmap originalBitmap, FilterCollection filters, int origWidth, int origHeight, int width, int height, bool wantPostProcessInfo, ref List<RGBAndCoordinate> postProcessInfo, ref int outWidth, ref int outHeight)
        {
            postProcessInfo = null;
            outWidth = 0; outHeight = 0;

            if (originalBitmap == null)
                return null;

            if (filters == null && width == origWidth && height == origHeight)
                return originalBitmap;

            if (width != origWidth || height != origHeight)
            {
                RenderSettings renderSettings;

                if (originalBitmap.PixelFormat != Settings.Values.StandardColorPixelFormat)
                {
                    renderSettings = new RenderSettings()
                    {
                        PixelFormat = originalBitmap.PixelFormat,
                        Interpolation = Settings.Values.InterpolationMode,
                        Smoothing = Settings.Values.SmoothingMode,
                        RenderingMethod = Settings.Values.InternalRendering
                    };
                }
                else
                {
                    renderSettings = Settings.Values.StandardRenderSettings;
                }

                originalBitmap = originalBitmap.CreateBitmap(width, height, renderSettings);
            }

            return filters.Apply(this._fileName, originalBitmap, false, wantPostProcessInfo, ref postProcessInfo, ref outWidth, ref outHeight);
        }
        
        protected void _filters_GenerationChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (Settings.Values.WantFilterThumbnails)
            {
                if (ThumbnailObject != null)
                {
                    ThumbnailObject.ClearThumbnailSilently();
                }
            }
        }

        protected void _filters_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (Settings.Values.WantFilterThumbnails)
            {
                if (ThumbnailObject != null)
                {
                    ThumbnailObject.ClearThumbnailSilently();
                }
            }
        }

        public double Similarity(IFileImage f2, int maxWidth, int maxHeight, bool makeCopies)
        {
            if (this == f2)
                return 0D;

            Bitmap bm1 = (makeCopies) ? (Bitmap)this.EnsureBitmapLoaded().Clone() : this.EnsureBitmapLoaded();
            Bitmap bm2 = (makeCopies) ? (Bitmap)f2.EnsureBitmapLoaded().Clone() : f2.EnsureBitmapLoaded();
            double aspect1 = this.AspectRatio;
            double aspect2 = f2.AspectRatio;
            if (makeCopies)
            {
                bm1 = new Bitmap(bm1, bm1.Width, bm1.Height);
                bm2 = new Bitmap(bm2, bm2.Width, bm2.Height);
            }
            return DetermineSimilarity.Similarity(bm1, bm2, this.FileName, f2.FileName, maxWidth, maxHeight, aspect1, aspect2, makeCopies);
        }
    }
}
